﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;
using M3G;

namespace MyTestProject {
    public class TestAnimationEvent {

        [Fact]
        public void TestConstructor () {
            var evnt = new AnimationEvent ();

            Assert.Equal (-1, evnt.EventTime);
            Assert.Equal (0, evnt.EventID);
            Assert.Equal (null, evnt.EventSource);
            Assert.Equal (null, evnt.EventTrack);
        }


        /**
         * もっとも簡単なアニメーションイベントのテスト1
         * 基本的な動作の確認用
         * */
        [Fact]
        public void TestFindAnimationEvents1 () {
            var kseq = new KeyframeSequence (4, 1, KeyframeSequence.Step);
            kseq.AddEvent (100, 1); // (time, id)
            kseq.Duration = 500;

            var ctrl = new AnimationController ();
            var track = new AnimationTrack (kseq, AnimationTrack.Alpha);
            track.SetController (ctrl);

            var lgh = new Light ();
            lgh.AddAnimationTrack (track);


            var events = lgh.FindAnimationEvents (0, 100, 0xffffffff);

            Assert.Equal (1, events.Length);
            Assert.Equal (100, events[0].EventTime);
            Assert.Equal (1, events[0].EventID);
            Assert.Equal (lgh, events[0].EventSource);
            Assert.Equal (track, events[0].EventTrack);
        }

        /**
         * もっとも簡単なアニメーションイベントのテスト2
         * オリジナルの方のFindAnimationEvent()のテスト
         * (この実装では原則戻り値で結果を受け取る独自形式の方を使用する)
         * */
        [Fact]
        public void TestFindAnimationEvents2 () {
            var kseq = new KeyframeSequence (4, 1, KeyframeSequence.Step);
            kseq.AddEvent (100, 1); // (time, id)
            kseq.Duration = 500;

            var ctrl = new AnimationController ();
            var track = new AnimationTrack (kseq, AnimationTrack.Alpha);
            track.SetController (ctrl);

            var lgh = new Light ();
            lgh.AddAnimationTrack (track);
            lgh.AddAnimationTrack (track);

            var events = new AnimationEvent[1];
            Assert.Equal (2, lgh.FindAnimationEvents (0, 100, 0xffffffff, events));

            Assert.Equal (100, events[0].EventTime);
            Assert.Equal (1, events[0].EventID);
            Assert.Equal (lgh, events[0].EventSource);
            Assert.Equal (track, events[0].EventTrack);
        }

        /**
         * リピートモードが正しく処理されイベント時間がワールド時間で記録されて返ってくる事を確認するテスト
         * */
        [Fact]
        public void TestEventTime () {
            var kseq = new KeyframeSequence (4, 1, KeyframeSequence.Step);
            kseq.AddEvent (100, 1); // (time, id)
            kseq.Duration = 500;
            kseq.RepeatMode = KeyframeSequence.Loop;

            var ctrl = new AnimationController ();
            var track = new AnimationTrack (kseq, AnimationTrack.Alpha);
            track.SetController (ctrl);

            var lgh = new Light ();
            lgh.AddAnimationTrack (track);


            var events = lgh.FindAnimationEvents (500, 1000, 0xffffffff);

            Assert.Equal (1, events.Length);
            Assert.Equal (600, events[0].EventTime);   // <-- ここ
            Assert.Equal (1, events[0].EventID);
        }

        /**
         * その時刻アニメーションコントローラーが有効なイベントのみ返ってくる事を確認するテスト
         * */
        [Fact]
        public void TestEventAndAnimationController () {
            var kseq = new KeyframeSequence (4, 1, KeyframeSequence.Step);
            kseq.AddEvent (100, 1); // (time, id)
            kseq.Duration = 500;
            kseq.RepeatMode = KeyframeSequence.Loop;

            var ctrl1 = new AnimationController ();
            var ctrl2 = new AnimationController ();
            ctrl1.SetActiveInterval (0, 500);
            ctrl2.SetActiveInterval (500, 1000);

            var track1 = new AnimationTrack (kseq, AnimationTrack.Alpha);
            var track2 = new AnimationTrack (kseq, AnimationTrack.Alpha);
            track1.SetController (ctrl1);
            track2.SetController (ctrl2);

            var lgh = new Light ();
            lgh.AddAnimationTrack (track1);
            lgh.AddAnimationTrack (track2);


            var events = lgh.FindAnimationEvents (500, 1000, 0xffffffff);

            // t=600でctr1は有効でないので無視されてctr2のtrack2のみ返ってくる
            Assert.Equal (1, events.Length);
            Assert.Equal (600, events[0].EventTime);
            Assert.Equal (track2, events[0].EventTrack);
        }

        /**
          * アニメーションイベントが時間順で整列している事を確認するテスト
          * */
        [Fact]
        public void TestEventsAreOrdered () {
            var kseq1 = new KeyframeSequence (4, 1, KeyframeSequence.Step);
            var kseq2 = new KeyframeSequence (4, 1, KeyframeSequence.Step);
            var kseq3 = new KeyframeSequence (4, 1, KeyframeSequence.Step);
            kseq1.AddEvent (200, 2); // (time, id)
            kseq2.AddEvent (100, 1); // (time, id)
            kseq3.AddEvent (300, 3); // (time, id)
            kseq1.Duration = 500;
            kseq2.Duration = 500;
            kseq3.Duration = 500;

            var ctrl = new AnimationController ();
            var track1 = new AnimationTrack (kseq1, AnimationTrack.Alpha);
            var track2 = new AnimationTrack (kseq2, AnimationTrack.Alpha);
            var track3 = new AnimationTrack (kseq3, AnimationTrack.Alpha);
            track1.SetController (ctrl);
            track2.SetController (ctrl);
            track3.SetController (ctrl);

            var lgh = new Light ();
            lgh.AddAnimationTrack (track1);
            lgh.AddAnimationTrack (track2);
            lgh.AddAnimationTrack (track3);

            var events = lgh.FindAnimationEvents (0, 500, 0xffffffff);

            // Found
            Assert.Equal (3, events.Length);

            Assert.Equal (1, events[0].EventID);
            Assert.Equal (2, events[1].EventID);
            Assert.Equal (3, events[2].EventID);
            Assert.Equal (100, events[0].EventTime);
            Assert.Equal (200, events[1].EventTime);
            Assert.Equal (300, events[2].EventTime);
        }


        /**
         * シーンをトラバースして到達可能な全イベントを回収出来ることを確認するテスト
         * */
        [Fact]
        public void TestTraverseAllNode () {
            var kseq = new KeyframeSequence (4, 1, KeyframeSequence.Step);
            kseq.AddEvent (100, 1); // (time, id)
            kseq.AddEvent (200, 2); // (time, id)
            kseq.Duration = 500;
            kseq.RepeatMode = KeyframeSequence.Loop;

            var ctrl = new AnimationController ();
            var track1 = new AnimationTrack (kseq, AnimationTrack.Alpha);
            var track2 = new AnimationTrack (kseq, AnimationTrack.Alpha);
            track1.SetController (ctrl);
            track2.SetController (ctrl);

            var lgh = new Light ();
            lgh.AddAnimationTrack (track1);

            var cam = new Camera ();
            cam.AddAnimationTrack (track2);

            var wld = new World ();
            wld.AddChild (lgh);
            wld.AddChild (cam);


            var events = wld.FindAnimationEvents (0, 100, 0xffffffff);

            // Found
            Assert.Equal (2, events.Length);

            Assert.Equal (1, events[0].EventID);
            Assert.Equal (100, events[0].EventTime);
            Assert.Equal (lgh, events[0].EventSource);
            Assert.Equal (track1, events[0].EventTrack);

            Assert.Equal (1, events[1].EventID);
            Assert.Equal (100, events[1].EventTime);
            Assert.Equal (cam, events[1].EventSource);
            Assert.Equal (track2, events[1].EventTrack);
        }

        /**
         * 条件を満たさないイベントを検出しないことを確認するテスト
         * */
        [Fact]
        public void TestIgnoreInvalidAnimationEvent () {
            var kseq1 = new KeyframeSequence (4, 1, KeyframeSequence.Step);
            var kseq2 = new KeyframeSequence (4, 1, KeyframeSequence.Step);
            kseq1.AddEvent (100, 1); //  --> マスクで除外
            kseq1.AddEvent (200, 2); //  --> コントローラーの再生速度を変更して時間外に除外
            kseq1.Duration = 500;
            kseq1.RepeatMode = KeyframeSequence.Loop;
            kseq2.AddEvent (100, 0xff); // --> コントローラーをセットしないので除外
            kseq2.Duration = 500;
            kseq2.RepeatMode = KeyframeSequence.Loop;

            var ctrl = new AnimationController ();
            ctrl.SetSpeed (0.5f, 0);

            var track1 = new AnimationTrack (kseq1, AnimationTrack.Alpha);
            track1.SetController (ctrl);

            var track2 = new AnimationTrack (kseq2, AnimationTrack.Alpha);

            var lgh = new Light ();
            lgh.AddAnimationTrack (track1);
            lgh.AddAnimationTrack (track2);

            var events = lgh.FindAnimationEvents (0, 200, 0xffffffe);

            // Not Found
            Assert.Equal (0, events.Length);
        }
    }
}
